﻿using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using StackExchange.Redis;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace PasteTimer
{
    ///// <summary>
    ///// 程序内缓存 不会自动过期，需要手动删除 注意！！！
    ///// </summary>
    //public class AppMemory : IAppCache,IDisposable
    //{

    //    /// <summary>
    //    /// 缓冲报表部分
    //    /// </summary>
    //    private Dictionary<string, Dictionary<string, int>> hashtable;

    //    private readonly IMemoryCache _table;

    //    /// <summary>
    //    /// 
    //    /// </summary>
    //    public AppMemory(IMemoryCache table)
    //    {
    //        _table = table;
    //        hashtable = new Dictionary<string, Dictionary<string, int>>();
    //    }

    //    /// <summary>
    //    /// 
    //    /// </summary>
    //    /// <returns></returns>
    //    public bool ModelRedis()
    //    {
    //        return false;
    //    }

    //    /// <summary>
    //    /// 添加某一个数据
    //    /// </summary>
    //    /// <param name="key"></param>
    //    /// <param name="felid"></param>
    //    /// <param name="num"></param>
    //    public void ReportAddTick(string key, string felid, int num = 1)
    //    {
    //        if (hashtable.ContainsKey(key))
    //        {
    //            var dic = hashtable[key] as Dictionary<string, int>;
    //            if (dic != null)
    //            {
    //                if (dic.ContainsKey(felid))
    //                {
    //                    dic[felid]++;
    //                }
    //                else
    //                {
    //                    dic.Add(felid, num);
    //                }
    //            }
    //        }
    //        else
    //        {
    //            var dic = new Dictionary<string, int>();
    //            dic.Add(felid, num);
    //            hashtable.Add(key, dic);
    //        }
    //    }

    //    /// <summary>
    //    /// 
    //    /// </summary>
    //    /// <param name="key"></param>
    //    /// <returns></returns>
    //    public Task<Dictionary<string, int>> ReportReadTimeAsync(string key)
    //    {
    //        return Task.FromResult(Task.Run(() =>
    //        {
    //            if (hashtable.ContainsKey(key))
    //            {
    //                var items = hashtable[key];
    //                return items;
    //            }
    //            else
    //            {
    //                return null;
    //            }
    //        }).Result);

    //    }

    //    /// <summary>
    //    /// 
    //    /// </summary>
    //    /// <param name="key"></param>
    //    /// <param name="felid"></param>
    //    /// <returns></returns>
    //    public Task<int> ReportReadValueAsync(string key, string felid)
    //    {
    //        return Task.FromResult(Task.Run(() =>
    //        {
    //            if (hashtable.ContainsKey(key))
    //            {
    //                var val = hashtable[key];
    //                if (val != null && val.ContainsKey(felid))
    //                {
    //                    return val[felid];
    //                }
    //            }

    //            return 0;
    //        }).Result);
    //    }

    //    /// <summary>
    //    /// 读取所有的key
    //    /// </summary>
    //    /// <returns></returns>
    //    public Task<string[]> ReportKeysAsync(string name = "report")
    //    {
    //        return Task.FromResult(Task.Run(() =>
    //        {
    //            var keys = hashtable.Keys.ToArray();
    //            return keys;
    //        }).Result);
    //    }

    //    /// <summary>
    //    /// 查看某一个hash子key的长度
    //    /// </summary>
    //    /// <returns></returns>
    //    public Task<long> ReportTimeLengthAsync(string key)
    //    {
    //        return Task.FromResult(Task.Run(() =>
    //        {
    //            if (hashtable.ContainsKey(key))
    //            {
    //                return (long)hashtable[key].Keys.Count;
    //            }
    //            return 0;
    //        }).Result);
    //    }

    //    /// <summary>
    //    /// 删除主key
    //    /// </summary>
    //    /// <param name="key"></param>
    //    public void ReportDelete(string key)
    //    {
    //        if (hashtable.ContainsKey(key))
    //        {
    //            hashtable.Remove(key);
    //        }
    //    }

    //    /// <summary>
    //    /// 删除某一个二级key
    //    /// </summary>
    //    /// <param name="key"></param>
    //    /// <param name="felid"></param>
    //    public void ReportDeleteTime(string key, string felid)
    //    {
    //        if (hashtable.ContainsKey(key))
    //        {
    //            var htab = hashtable[key] as Dictionary<string, int>;
    //            if (htab != null)
    //            {
    //                if (htab.ContainsKey(felid))
    //                {
    //                    htab.Remove(felid);
    //                }

    //                if (htab.Keys.Count == 0)
    //                {
    //                    hashtable.Remove(key);
    //                }
    //            }
    //        }
    //    }


    //    /// <summary>
    //    /// 
    //    /// </summary>
    //    /// <param name="key"></param>
    //    /// <param name="val"></param>
    //    /// <param name="second"></param>
    //    public void SetString(string key, string val, int second = 3600)
    //    {

    //        using (var wcache = _table.CreateEntry(key))
    //        {
    //            wcache.Value = val;
    //             wcache.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(second);
    //        }
    //    }

    //    /// <summary>
    //    /// 
    //    /// </summary>
    //    /// <param name="key"></param>
    //    /// <returns></returns>
    //    public bool KeyExits(string key)
    //    {
    //       return _table.TryGetValue(key, out var hashtable);
    //    }

    //    /// <summary>
    //    /// 
    //    /// </summary>
    //    /// <param name="key"></param>
    //    /// <returns></returns>
    //    public Task<bool> KeyExitsAsync(string key)
    //    {
    //        return Task.Run<bool>(() => {
    //           return _table.TryGetValue<string>(key, out var val);
    //        });
    //    }


    //    /// <summary>
    //    /// 
    //    /// </summary>
    //    /// <typeparam name="T"></typeparam>
    //    /// <param name="key"></param>
    //    /// <param name="t"></param>
    //    /// <param name="second"></param>
    //    public void SetObject<T>(string key, T t, int second = 3600)
    //    {
    //        var strval = Newtonsoft.Json.JsonConvert.SerializeObject(t);
    //        SetString(key, strval);
    //    }

    //    /// <summary>
    //    /// 
    //    /// </summary>
    //    /// <param name="key"></param>
    //    /// <returns></returns>
    //    /// <exception cref="NotImplementedException"></exception>
    //    public Task<string> ReadStringAsync(string key)
    //    {
    //        return Task.Run<string>(() => {
    //            _table.TryGetValue<string>(key, out var val);
    //            return val;
    //        });
    //    }

    //    /// <summary>
    //    /// 
    //    /// </summary>
    //    /// <param name="key"></param>
    //    public void RemoveKey(string key)
    //    {
    //        _table.Remove(key);
    //    }


    //    /// <summary>
    //    /// 
    //    /// </summary>
    //    /// <typeparam name="T"></typeparam>
    //    /// <param name="key"></param>
    //    /// <returns></returns>
    //    public Task<T> ReadObject<T>(string key)
    //    {
    //        return Task.Run<T>(() => {
    //            _table.TryGetValue<string>(key, out var val);
    //            if (!String.IsNullOrEmpty(val))
    //            {
    //                return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(val);
    //            }
    //            return default(T);
    //        });
    //    }

    //    /// <summary>
    //    /// 
    //    /// </summary>
    //    public void Dispose()
    //    {
    //        _table.Dispose();
    //    }
    //}

    /// <summary>
    /// 
    /// </summary>
    public class AppRedis : IAppCache,IDisposable
    {
        //private readonly IConnectionMultiplexer _connectionMultiplexer;

        private readonly RedisConfig _config;
        private readonly IDatabase _table;

        /// <summary>
        /// Key的前缀
        /// </summary>
        private string PrefixHead = String.Empty;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="options"></param>
        /// <exception cref="Exception"></exception>
        public AppRedis(IOptions<RedisConfig> options)
        {
            _config = options.Value;

            var oaconfig = ConfigurationOptions.Parse(_config.MainConnection);

            PrefixHead = _config.Prefix;

            oaconfig.ClientName = nameof(_config.MainConnection);

            //多路链接复用器
            ConnectionMultiplexer _multiplexer = ConnectionMultiplexer.Connect(oaconfig);

            if (_multiplexer != null && _multiplexer.IsConnected)
            {
                _table = _multiplexer.GetDatabase();

            }
            else
            {
                throw new Exception("Redis is not Connected");
            }

        }

        private string setKey(string key)
        {
            return PrefixHead + key;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public bool ModelRedis()
        {
            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        public void RemoveKey(string key) {

            _table.KeyDeleteAsync(setKey(key));

        }

        #region Hash模块

        //其实这里可以使用hash搜索实现，但是为了和应用缓存一致，就做了一个转换
        private const string ReportKey = "report";

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="felid"></param>
        /// <param name="num"></param>
        public void HashIncrementAsync(string key, string felid, int num = 1)
        {
            _table.HashIncrementAsync(setKey(key), felid, num);

            //_table.HashIncrementAsync(ReportKey, key);

            //
            //collect:run time:taskid num
            //collect:err time:taskid num

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<string[]> HashKeysAsync(string key)
        {
            var keys = await _table.HashKeysAsync(setKey(key));
            var list = new List<string>();
            foreach (var item in keys)
            {
                list.Add(item.ToString());
            }
            return list.ToArray();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<Dictionary<string, int>> HashGetAllAsync(string key)
        {
            var vals = await _table.HashGetAllAsync(setKey(key));
            var dic = new Dictionary<string, int>();
            if (vals != null && vals.Length > 0)
            {
                foreach (var item in vals)
                {
                    var dkey = item.Name.ToString();
                    int.TryParse(item.Value.ToString(), out var dval);
                    if (!dic.ContainsKey(dkey))
                    {
                        dic.Add(dkey, dval);
                    }
                }
            }
            return dic;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="felid"></param>
        /// <returns></returns>
        public async Task<int> HashGetAsync(string key, string felid)
        {

            var val = await _table.HashGetAsync(setKey(key), felid);
            int.TryParse(val.ToString(), out var rval);
            return rval;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<long> HashLengthAsync(string key)
        {
            var len = await _table.HashLengthAsync(setKey(key));
            return len;
        }

        ///// <summary>
        ///// 
        ///// </summary>
        ///// <param name="key"></param>
        //public void ReportDelete(string key)
        //{
        //    _table.KeyDeleteAsync(setKey(key));

        //    _table.HashDeleteAsync(ReportKey,key);
        //}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="felid"></param>
        public void HashDeleteAsync(string key, string felid)
        {
            _table.HashDeleteAsync(setKey(key), felid);
        }


        ///// <summary>
        ///// 
        ///// </summary>
        ///// <param name="name"></param>
        ///// <returns></returns>
        //public async Task<string[]> ReportKeysAsync(string name = "report")
        //{

        //    var result = await HashKeysAsync(ReportKey);
        //    return result;
        //}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="val"></param>
        /// <param name="second"></param>
        /// <exception cref="NotImplementedException"></exception>
        public void SetString(string key, string val, int second = 3600)
        {
            _table.StringSetAsync(setKey(key), val,TimeSpan.FromSeconds(second));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="t"></param>
        /// <param name="second"></param>
        public void SetObject<T>(string key, T t, int second = 3600)
        {
            var str=Newtonsoft.Json.JsonConvert.SerializeObject(t);
            SetString(key,str,second);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public Task<string> ReadStringAsync(string key)
        {
            return Task.Run<string>(async () => {
                return await _table.StringGetAsync(setKey(key));
            });

        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public Task<T> ReadObject<T>(string key)
        {
            return Task.Run<T>(async () => {
                var str = await _table.StringGetAsync(key);
                if (!string.IsNullOrEmpty(str))
                {
                    return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(str);
                }
                return default(T);
            });
        }

        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool KeyExits(string key)
        {
            return _table.KeyExists(key);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public async Task<bool> KeyExitsAsync(string key)
        {
            return await _table.KeyExistsAsync(key);
        }


        #endregion
    }


    /// <summary>
    /// 
    /// </summary>
    public interface IAppCache
    {
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        bool ModelRedis();

        /// <summary>
        /// 添加某一个数据
        /// </summary>
        /// <param name="key"></param>
        /// <param name="felid"></param>
        /// <param name="num"></param>
        void HashIncrementAsync(string key, string felid, int num = 1);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        Task<Dictionary<string, int>> HashGetAllAsync(string key);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="felid"></param>
        /// <returns></returns>
        Task<int> HashGetAsync(string key, string felid);

        ///// <summary>
        ///// 读取所有的key
        ///// </summary>
        ///// <returns></returns>
        //Task<string[]> ReportKeysAsync(string name = "report");

        /// <summary>
        /// 查看某一个hash子key的长度
        /// </summary>
        /// <returns></returns>
        Task<long> HashLengthAsync(string key);

        ///// <summary>
        ///// 删除主key
        ///// </summary>
        ///// <param name="key"></param>
        //void ReportDelete(string key);

        /// <summary>
        /// 删除某一个二级key
        /// </summary>
        /// <param name="key"></param>
        /// <param name="felid"></param>
        void HashDeleteAsync(string key, string felid);


        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <param name="val"></param>
        /// <param name="second"></param>
        void SetString(string key,string val,int second=3600);


        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="t"></param>
        /// <param name="second"></param>
        void SetObject<T>(string key,T t, int second = 3600);

        /// <summary>
        /// 删除一个key String
        /// </summary>
        /// <param name="key"></param>
        void RemoveKey(string key);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        Task<string> ReadStringAsync(string key);

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        Task<T> ReadObject<T>(string key);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        bool KeyExits(string key);

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        Task<bool> KeyExitsAsync(string key);
    }

}
